package handler

import (
    "encoding/json"
    "io"
    "net/http"
    "strings"
    "time"

	"dns-tools/pkg/dns"
	"dns-tools/pkg/validator"
	"dns-tools/pkg/verify"
	"github.com/sirupsen/logrus"
)

// DCVVerifyHandler DCV 验证处理器（/api/dcv/verify）
type DCVVerifyHandler struct {
	verifier *verify.BatchVerifier
	logger   *logrus.Logger
}

// DCVHandler 创建 DCV 验证处理器
func DCVHandler(logger *logrus.Logger) *DCVVerifyHandler {
	// 使用 Google Public DNS 解析器（8.8.8.8/8.8.4.4）
	resolver := dns.NewGoogleDNSResolver()
	doh := verify.NewDoHDNSResolver(resolver)

	return &DCVVerifyHandler{
		verifier: verify.NewBatchVerifier(
			verify.WithMaxConcurrency(20),
			verify.WithTimeout(10*time.Second),
			verify.WithDNSResolver(doh),
		),
		logger: logger,
	}
}

// Handle 处理批量验证请求
func (h *DCVVerifyHandler) Handle(w http.ResponseWriter, r *http.Request) {
	// 只接受 POST 请求
	if r.Method != http.MethodPost {
		h.sendError(w, http.StatusMethodNotAllowed, "仅支持 POST 请求")
		return
	}

	// 限制请求体大小至 200KB
	r.Body = http.MaxBytesReader(w, r.Body, 200*1024)

    // 解析请求：直接接收数组 JSON（不再支持 {"validation": [...]} 包装）
    type inputItem struct {
        Domain  string `json:"domain"`
        Method  string `json:"method"`
        Host    string `json:"host"`
        Value   string `json:"value"`
        Link    string `json:"link"`
        Name    string `json:"name"`
        Content string `json:"content"`
    }

    body, err := io.ReadAll(r.Body)
    if err != nil {
        h.logger.WithError(err).Error("Failed to read request body")
        h.sendError(w, http.StatusBadRequest, "请求体无效")
        return
    }

    var items []inputItem
    if err := json.Unmarshal(body, &items); err != nil {
        h.logger.WithError(err).Error("Failed to decode request (expect JSON array)")
        h.sendError(w, http.StatusBadRequest, "请求格式无效：期望 JSON 数组")
        return
    }

    if len(items) == 0 {
        h.sendError(w, http.StatusBadRequest, "未提供验证任务")
        return
    }

    if len(items) > 100 {
        h.sendError(w, http.StatusBadRequest, "任务数量过多（最多 100 个）")
        return
    }

	// 记录请求信息
    h.logger.WithFields(logrus.Fields{
        "task_count": len(items),
        "remote_ip":  r.RemoteAddr,
    }).Info("Processing batch verification request")

	// 为组合逻辑准备结构：对 DNS 任务支持两次查询（root 优先，子域回落）
	type dnsPlan struct {
		keyDisplay string // 结果 map 的 key（原始域名，保留原样大小写/Unicode）
		method     verify.VerificationMethod
		expected   string
		// ascii 查询 FQDN 索引（在 tasks 中的下标），-1 表示未安排
		idxRoot int
		idxSub  int
		// 展示用的查询名（使用原始域名显示，不转码）
		qDisplayRoot string
		qDisplaySub  string
	}

    tasks := make([]verify.VerificationTask, 0, len(items)*2)
    dnsPlans := make([]dnsPlan, 0)
    httpIdx := make([]int, 0) // 非 DNS 的任务 index 映射到 items 顺序
    for i, v := range items {
		// 原始显示域名（仅做 Trim，保持大小写/Unicode 原样）
		displayDomain := strings.TrimSpace(v.Domain)
		method := strings.ToLower(strings.TrimSpace(v.Method))

		switch method {
		case "txt", "cname":
			// host 处理：@ 视为空
			host := strings.TrimSpace(v.Host)
			if host == "@" {
				host = ""
			}

			// 如果主机记录中包含点（例如 "_dnsauth.test"），表示已经带有域名前缀
			// 此时应跳过子域名回落检测，只按 root 构造查询
			hostHasDot := strings.Contains(host, ".")

			// 用于查询的 ASCII 域名与根域
			cleaned := validator.CleanDomainInput(displayDomain)
			asciiDomain := validator.NormalizeDomain(cleaned)
			root, _ := validator.ExtractRootDomain(cleaned) // 返回 ASCII 根域

			// 判断是否为通配符域名
			isWildcard := strings.HasPrefix(asciiDomain, "*.")
			
			// 对于通配符域名，去掉通配符前缀后判断是否为子域
			domainForSubCheck := asciiDomain
			if isWildcard {
				domainForSubCheck = strings.TrimPrefix(asciiDomain, "*.")
			}
			
			// 判断是否为子域（通配符去掉后判断）
			isSub := false
			if domainForSubCheck != root {
				isSub = true
			}
			
			// 获取第一个标签（用于判断 www）
			firstLabel := domainForSubCheck
			if dot := strings.IndexByte(domainForSubCheck, '.'); dot > 0 {
				firstLabel = domainForSubCheck[:dot]
			}
			
			// 子域查询逻辑：
			// 1. 通配符根域名（*.example.com）不需要子域查询
			// 2. 通配符子域名（*.sub.example.com）需要子域查询到 *.example.com 级别
			// 3. 普通子域名（sub.example.com）需要子域查询，除非是 www
			// 4. 根域名不需要子域查询
			// 5. 若 host 自身包含点（如 "_dnsauth.test"），表示带有域名前缀，跳过子域查询
			enableSub := isSub && strings.ToLower(firstLabel) != "www" && !hostHasDot

			// 构造 FQDN（ASCII 与展示用）
			join := func(h, d string) string {
				if h == "" {
					return d
				}
				return h + "." + d
			}
			
			// 展示用 root：从原始域名截取最后 N 个标签
			labelsDisp := strings.Split(displayDomain, ".")
			labelsRootASCII := strings.Split(root, ".")
			n := len(labelsRootASCII)
			if n > len(labelsDisp) {
				n = len(labelsDisp)
			}
			displayRoot := strings.Join(labelsDisp[len(labelsDisp)-n:], ".")

			// 处理域名用于查询
			// 通配符域名需要去掉 *. 前缀进行查询
			asciiDomainForQuery := asciiDomain
			displayDomainForQuery := displayDomain
			if isWildcard {
				// 去掉通配符前缀进行查询
				asciiDomainForQuery = strings.TrimPrefix(asciiDomain, "*.")
				if strings.HasPrefix(displayDomain, "*.") {
					displayDomainForQuery = strings.TrimPrefix(displayDomain, "*.")
				}
			}

			// 构建查询 FQDN
			// 根域查询总是使用根域名
			fqdnRootASCII := join(host, root)
			fqdnRootDisp := join(host, displayRoot)
			
			// 子域查询的 FQDN
			fqdnSubASCII := ""
			fqdnSubDisp := ""
			if enableSub {
				// 子域查询使用处理后的域名（通配符已去掉）
				fqdnSubASCII = join(host, asciiDomainForQuery)
				fqdnSubDisp = join(host, displayDomainForQuery)
			}

			// 安排 root 查询
			var vMethod verify.VerificationMethod
			if method == "txt" {
				vMethod = verify.MethodDNSTXT
			} else {
				vMethod = verify.MethodDNSCNAME
			}
			plan := dnsPlan{keyDisplay: displayDomain, method: vMethod, expected: strings.TrimSpace(v.Value), idxRoot: -1, idxSub: -1, qDisplayRoot: fqdnRootDisp, qDisplaySub: fqdnSubDisp}
			plan.idxRoot = len(tasks)
			tasks = append(tasks, verify.VerificationTask{Domain: fqdnRootASCII, Method: vMethod, ExpectedValue: plan.expected})
			if enableSub {
				plan.idxSub = len(tasks)
				tasks = append(tasks, verify.VerificationTask{Domain: fqdnSubASCII, Method: vMethod, ExpectedValue: plan.expected})
			}
			dnsPlans = append(dnsPlans, plan)

		case "file":
			link := strings.TrimSpace(v.Link)
			tasks = append(tasks, verify.VerificationTask{URL: link, Method: verify.MethodFile, ExpectedValue: v.Content})
			httpIdx = append(httpIdx, i)
		case "http":
			link := strings.TrimSpace(v.Link)
			if strings.HasPrefix(link, "//") {
				link = "http:" + link
			}
			tasks = append(tasks, verify.VerificationTask{URL: link, Method: verify.MethodHTTP, ExpectedValue: v.Content})
			httpIdx = append(httpIdx, i)
		case "https":
			link := strings.TrimSpace(v.Link)
			if strings.HasPrefix(link, "//") {
				link = "https:" + link
			}
			tasks = append(tasks, verify.VerificationTask{URL: link, Method: verify.MethodHTTPS, ExpectedValue: v.Content})
			httpIdx = append(httpIdx, i)
		default:
			// 跳过未知方法
		}
	}

	if len(tasks) == 0 {
		h.sendError(w, http.StatusBadRequest, "未提供有效的验证任务")
		return
	}

	// 执行批量验证
	start := time.Now()
	ctx := r.Context()
	results := h.verifier.ProcessBatch(ctx, tasks)
	duration := time.Since(start)

	// 组装最终结果（按输入项计数）
	resultMap := make(map[string]map[string]string)
	total := 0
	successCount := 0
	failedCount := 0

	// 先处理 DNS 计划
	for _, plan := range dnsPlans {
		total++
		if resultMap[plan.keyDisplay] == nil {
			resultMap[plan.keyDisplay] = make(map[string]string)
		}
		m := resultMap[plan.keyDisplay]

		var rootRes, subRes *verify.VerificationResult
		if plan.idxRoot >= 0 {
			rr := results[plan.idxRoot]
			rootRes = &rr
		}
		if plan.idxSub >= 0 {
			sr := results[plan.idxSub]
			subRes = &sr
		}

		// 成功优先级：root 命中优先；否则看 sub
		if rootRes != nil && rootRes.Success {
			m["matched"] = "true"
			if plan.method == verify.MethodDNSTXT || plan.method == verify.MethodDNSCNAME {
				m["value"] = rootRes.ActualValue
				m["query"] = plan.qDisplayRoot
			}
			successCount++
		} else if subRes != nil && subRes.Success {
			m["matched"] = "true"
			if plan.method == verify.MethodDNSTXT || plan.method == verify.MethodDNSCNAME {
				m["value"] = subRes.ActualValue
				m["query"] = plan.qDisplaySub
			}
			successCount++
		} else {
			m["matched"] = "false"
			// 失败时：value 保留 root 查询的实际值（若有），query 使用 root；并附带子域调试字段
			if plan.method == verify.MethodDNSTXT || plan.method == verify.MethodDNSCNAME {
				if rootRes != nil {
					m["value"] = rootRes.ActualValue
				}
				m["query"] = plan.qDisplayRoot
				if subRes != nil && plan.qDisplaySub != "" && plan.qDisplaySub != plan.qDisplayRoot {
					// 只在子域查询与根域查询不同时才添加子域查询信息
					m["query_sub"] = plan.qDisplaySub
					if subRes.ActualValue != "" {
						m["value_sub"] = subRes.ActualValue
					}
				}
			}
			failedCount++
		}
	}

	// 然后处理 HTTP/HTTPS/FILE（它们在 tasks 中的顺序与 results 对齐）。
    // 使用 httpIdx 映射处理内容类任务的结果
    // 计算 content 区的起始下标：等于所有 DNS 计划安排的任务数量
    dnsTasksCount := 0
    for _, p := range dnsPlans {
        if p.idxRoot >= 0 {
            dnsTasksCount++
        }
        if p.idxSub >= 0 {
            dnsTasksCount++
        }
    }
    
    for contentIndex, originalIndex := range httpIdx {
        if originalIndex < 0 || originalIndex >= len(items) {
            continue
        }
        v := items[originalIndex]
        method := strings.ToLower(strings.TrimSpace(v.Method))
        if method != "http" && method != "https" && method != "file" {
            continue
        }
        total++
        key := strings.TrimSpace(v.Domain)
        if resultMap[key] == nil {
            resultMap[key] = make(map[string]string)
        }
        m := resultMap[key]

        // 找到对应的 result
        idx := dnsTasksCount + contentIndex
        if idx < 0 || idx >= len(results) {
            continue
        }
        res := results[idx]

		// 填充字段
		if res.Success {
			m["matched"] = "true"
		} else {
			m["matched"] = "false"
		}
		m["content"] = res.ActualValue
		// link 字段策略：
		// - HTTP/HTTPS 不返回 link
		// - FILE 成功：返回 link（带协议）与 content
		// - FILE 失败：返回 link_https 与 link_http（若可用）
		if method == "file" {
			if res.Success {
				if res.URL != "" {
					m["link"] = res.URL
				}
			} else {
				if res.LinkHTTPS != "" {
					m["link_https"] = res.LinkHTTPS
				}
				if res.LinkHTTP != "" {
					m["link_http"] = res.LinkHTTP
				}
			}
		}

		if res.Success {
			successCount++
		} else {
			failedCount++
		}
	}

	// 记录响应信息
	h.logger.WithFields(logrus.Fields{
		"total":    len(results),
		"success":  successCount,
		"failed":   failedCount,
		"duration": duration.Seconds(),
	}).Info("Batch verification completed")

    // 根据是否全部成功决定响应格式
    if failedCount == 0 {
        h.sendSuccess(w, map[string]interface{}{
            "results":   resultMap,
            "total":     len(results),
            "success":   successCount,
            "failed":    failedCount,
            "duration":  duration.Milliseconds(),
            "timestamp": time.Now(),
        })
        return
    }

    // 失败：将每个域的详细情况平铺为 errors 方便前端展示
    details := make([]map[string]string, 0, len(resultMap))
    for dom, kv := range resultMap {
        item := map[string]string{"domain": dom}
        for k, v := range kv {
            item[k] = v
        }
        details = append(details, item)
    }
    msg := "批量验证失败：部分或全部验证未通过"
    h.sendErrorDetailed(w, http.StatusUnprocessableEntity, msg, details)
}

// sendSuccess 发送成功响应（HTTP 200 + code=1）
func (h *DCVVerifyHandler) sendSuccess(w http.ResponseWriter, data interface{}) {
    h.sendJSON(w, http.StatusOK, Response{
        Code: 1,
        Data: data,
    })
}

// sendError 发送错误响应（HTTP 200 + code=0）
// 说明：按产品约定，失败也使用 HTTP 200，由 code=0 表示失败。
//      status 参数暂保留用于未来特殊情况（当前忽略）。
func (h *DCVVerifyHandler) sendError(w http.ResponseWriter, status int, message string) {
    _ = status
    h.sendJSON(w, http.StatusOK, Response{
        Code: 0,
        Msg:  message,
    })
}

// sendErrorDetailed 发送带 errors 的错误响应
func (h *DCVVerifyHandler) sendErrorDetailed(w http.ResponseWriter, status int, message string, errors interface{}) {
    _ = status
    h.sendJSON(w, http.StatusOK, Response{
        Code:   0,
        Msg:    message,
        Errors: errors,
    })
}

// sendJSON 发送 JSON 响应
func (h *DCVVerifyHandler) sendJSON(w http.ResponseWriter, status int, data interface{}) {
	w.Header().Set("Content-Type", "application/json")
	// 防止任何形式的缓存
	w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
	w.Header().Set("Pragma", "no-cache")
	w.Header().Set("Expires", "0")
	w.WriteHeader(status)

	if err := json.NewEncoder(w).Encode(data); err != nil {
		h.logger.WithError(err).Error("Failed to encode response")
	}
}

// DCVVerifyMiddleware DCV 验证 CORS 中间件
func DCVVerifyMiddleware(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// 设置 CORS 头
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
		// 防止任何形式的缓存
		w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
		w.Header().Set("Pragma", "no-cache")
		w.Header().Set("Expires", "0")

		// 处理预检请求
		if r.Method == http.MethodOptions {
			w.WriteHeader(http.StatusOK)
			return
		}

		next(w, r)
	}
}
